package org.shuang.bai.service;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.Validate;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.runtime.MethodClosure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;

@Service
public class ScriptService implements ApplicationContextAware {

	private static final Logger logger = LoggerFactory.getLogger(ScriptService.class);

	private ApplicationContext applicationContext;

	@Resource
	private Environment env;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@SuppressWarnings("unchecked")
	public <T> T ref(String beanName) {
		Validate.notNull(beanName);
		return (T) this.applicationContext.getBean(beanName);
	}

	public <T> T ref(Class<T> clazz) {
		Validate.notNull(clazz);
		return this.applicationContext.getBean(clazz);
	}

	public String prop(String key) {
		return this.env.getProperty(key);
	}

	public void runScript(Path scriptPath, Map<String, Object> variables) {
		GroovyShell groovyShell = new GroovyShell();
		try {
			// this is just a demo, real code should cache script
			Script script = groovyShell.parse(scriptPath.toFile());
			Binding binding = new Binding();
			variables.forEach((key, value) -> binding.setProperty(key, value));

			// add closure to scripte
			binding.setProperty("prop", new MethodClosure(this, "prop"));
			binding.setProperty("ref", new MethodClosure(this, "ref"));
			binding.setProperty("logger", logger);
			script.setBinding(binding);
			script.run();
		} catch (CompilationFailedException | IOException e) {
			throw new RuntimeException(e);
		}
	}

	public void runScript(String scriptContent, Map<String, Object> variables) {
		GroovyShell groovyShell = new GroovyShell();
		try {
			// this is just a demo, real code should cache script
			Script script = groovyShell.parse(scriptContent);
			Binding binding = new Binding();
			variables.forEach((key, value) -> binding.setProperty(key, value));

			// add closure to scripte
			binding.setProperty("prop", new MethodClosure(this, "prop"));
			binding.setProperty("ref", new MethodClosure(this, "ref"));
			binding.setProperty("logger", logger);
			script.setBinding(binding);
			script.run();
		} catch (CompilationFailedException e) {
			throw new RuntimeException(e);
		}
	}
}
